package breaker

import (
	"errors"
	"sync"
	"time"
)

// 熔断器状态
const (
	StateClosed   = iota // 关闭状态，允许请求通过
	StateOpen            // 开启状态，请求被熔断
	StateHalfOpen        // 半开状态，允许部分请求通过以探测服务是否恢复
)

// FallbackFunc 定义全局降级函数类型，在多个熔断器实现中复用
type FallbackFunc func(err error) (interface{}, error)

// 错误定义
var (
	ErrCircuitOpen = errors.New("熔断器开启，请求被拒绝")
)

// CircuitBreaker 熔断器接口
type CircuitBreaker interface {
	// Execute 执行请求，如果熔断器开启则返回错误
	Execute(func() error) error
	// GetState 获取熔断器状态
	GetState() int
	// Reset 重置熔断器状态
	Reset()
}

// SimpleBreaker 简单熔断器实现
type SimpleBreaker struct {
	state            int           // 当前状态
	failureThreshold int           // 触发熔断的连续失败次数阈值
	successThreshold int           // 半开状态下恢复服务的连续成功次数阈值
	timeoutDuration  time.Duration // 熔断器从开启状态转为半开状态的超时时间
	lastStateChange  time.Time     // 最后一次状态变更时间
	failureCount     int           // 连续失败次数
	successCount     int           // 半开状态下连续成功次数
	mutex            sync.RWMutex  // 读写锁
}

// NewSimpleBreaker 创建一个简单熔断器
func NewSimpleBreaker(failureThreshold, successThreshold int, timeoutDuration time.Duration) *SimpleBreaker {
	return &SimpleBreaker{
		state:            StateClosed,
		failureThreshold: failureThreshold,
		successThreshold: successThreshold,
		timeoutDuration:  timeoutDuration,
		lastStateChange:  time.Now(),
		failureCount:     0,
		successCount:     0,
	}
}

// Execute 执行请求，如果熔断器开启则返回错误
func (sb *SimpleBreaker) Execute(fn func() error) error {
	state := sb.GetState()

	// 熔断器开启状态，拒绝请求
	if state == StateOpen {
		return ErrCircuitOpen
	}

	// 执行请求
	err := fn()

	// 更新熔断器状态
	sb.mutex.Lock()
	defer sb.mutex.Unlock()

	if err != nil {
		// 请求失败
		sb.onFailure()
	} else {
		// 请求成功
		sb.onSuccess()
	}

	return err
}

// GetState 获取熔断器当前状态
func (sb *SimpleBreaker) GetState() int {
	sb.mutex.RLock()
	defer sb.mutex.RUnlock()

	now := time.Now()

	// 如果当前是开启状态，且超过了超时时间，则转为半开状态
	if sb.state == StateOpen && now.Sub(sb.lastStateChange) >= sb.timeoutDuration {
		sb.state = StateHalfOpen
		sb.lastStateChange = now
	}

	return sb.state
}

// Reset 重置熔断器状态
func (sb *SimpleBreaker) Reset() {
	sb.mutex.Lock()
	defer sb.mutex.Unlock()

	sb.state = StateClosed
	sb.failureCount = 0
	sb.successCount = 0
	sb.lastStateChange = time.Now()
}

// onFailure 处理失败请求
func (sb *SimpleBreaker) onFailure() {
	switch sb.state {
	case StateClosed:
		// 关闭状态下，增加失败计数
		sb.failureCount++
		// 如果连续失败次数达到阈值，则开启熔断器
		if sb.failureCount >= sb.failureThreshold {
			sb.state = StateOpen
			sb.lastStateChange = time.Now()
		}
	case StateHalfOpen:
		// 半开状态下，任何失败都会重新开启熔断器
		sb.state = StateOpen
		sb.lastStateChange = time.Now()
		sb.successCount = 0
	}
}

// onSuccess 处理成功请求
func (sb *SimpleBreaker) onSuccess() {
	switch sb.state {
	case StateClosed:
		// 关闭状态下，重置失败计数
		sb.failureCount = 0
	case StateHalfOpen:
		// 半开状态下，增加成功计数
		sb.successCount++
		// 如果连续成功次数达到阈值，则关闭熔断器
		if sb.successCount >= sb.successThreshold {
			sb.state = StateClosed
			sb.lastStateChange = time.Now()
			sb.failureCount = 0
			sb.successCount = 0
		}
	}
}

// MetricsBreaker 带指标统计的熔断器
type MetricsBreaker struct {
	SimpleBreaker
	errorRate      float64 // 错误率阈值，超过此阈值触发熔断
	totalRequests  int     // 统计窗口内的总请求数
	failedRequests int     // 统计窗口内的失败请求数
	windowSize     int     // 统计窗口大小
	requestResults []bool  // 请求结果记录，true表示成功，false表示失败
	windowIndex    int     // 当前窗口索引
}

// NewMetricsBreaker 创建一个带指标统计的熔断器
func NewMetricsBreaker(errorRate float64, windowSize int, timeoutDuration time.Duration) *MetricsBreaker {
	return &MetricsBreaker{
		SimpleBreaker: SimpleBreaker{
			state:            StateClosed,
			failureThreshold: 1, // 这里不使用连续失败次数，而是使用错误率
			successThreshold: 5,
			timeoutDuration:  timeoutDuration,
			lastStateChange:  time.Now(),
		},
		errorRate:      errorRate,
		totalRequests:  0,
		failedRequests: 0,
		windowSize:     windowSize,
		requestResults: make([]bool, windowSize),
		windowIndex:    0,
	}
}

// Execute 执行请求，如果熔断器开启则返回错误
func (mb *MetricsBreaker) Execute(fn func() error) error {
	state := mb.GetState()

	// 熔断器开启状态，拒绝请求
	if state == StateOpen {
		return ErrCircuitOpen
	}

	// 执行请求
	err := fn()

	// 更新熔断器状态
	mb.mutex.Lock()
	defer mb.mutex.Unlock()

	// 更新统计窗口
	if mb.totalRequests < mb.windowSize {
		mb.totalRequests++
	} else {
		// 移除最老的记录
		if mb.requestResults[mb.windowIndex] == false {
			mb.failedRequests--
		}
	}

	// 记录新的结果
	mb.requestResults[mb.windowIndex] = (err == nil)
	if err != nil {
		mb.failedRequests++
	}

	// 更新窗口索引
	mb.windowIndex = (mb.windowIndex + 1) % mb.windowSize

	// 计算错误率并更新状态
	if mb.totalRequests > 0 {
		errorRate := float64(mb.failedRequests) / float64(mb.totalRequests)
		if errorRate >= mb.errorRate && mb.state == StateClosed {
			// 错误率超过阈值，开启熔断器
			mb.state = StateOpen
			mb.lastStateChange = time.Now()
		} else if mb.state == StateHalfOpen {
			if err != nil {
				// 半开状态下，任何失败都会重新开启熔断器
				mb.state = StateOpen
				mb.lastStateChange = time.Now()
				mb.successCount = 0
			} else {
				// 半开状态下，增加成功计数
				mb.successCount++
				// 如果连续成功次数达到阈值，则关闭熔断器
				if mb.successCount >= mb.successThreshold {
					mb.state = StateClosed
					mb.lastStateChange = time.Now()
					mb.failureCount = 0
					mb.successCount = 0
					// 重置统计窗口
					mb.totalRequests = 0
					mb.failedRequests = 0
					for i := range mb.requestResults {
						mb.requestResults[i] = true
					}
				}
			}
		}
	}

	return err
}

// Reset 重置熔断器状态和统计数据
func (mb *MetricsBreaker) Reset() {
	mb.mutex.Lock()
	defer mb.mutex.Unlock()

	mb.state = StateClosed
	mb.failureCount = 0
	mb.successCount = 0
	mb.lastStateChange = time.Now()
	mb.totalRequests = 0
	mb.failedRequests = 0
	mb.windowIndex = 0
	for i := range mb.requestResults {
		mb.requestResults[i] = true
	}
}
